﻿using System;
using System.Collections.Generic;
using Layers;
using Layers.UI;
using System.IO;

namespace HoldemGame
{
    public abstract class HoldemBrains : IBrains
    {
        public abstract string ID { get; }

        public abstract BrainAction Think(IGameState game, GamePlayer me);
        public TextWriter Out() { return MyLogger.DefaultWriter;  }
        public override string ToString()
        {
            return ID;
        }
    }

    public class RandomBrains:HoldemBrains
    {
        public static void Register()
        {
            SelectFrom.Providers.Add("Brains", new ValueProvider(new object[]{new RandomBrains(), new Raise50Brains(), new RaiseVsBankBrains()}));
        }

        #region IBrains Members

        public override BrainAction Think(IGameState game, GamePlayer me)
        {
            Random rnd = new Random((int)DateTime.Now.Ticks);
            for (; ; )
            {
                int x = rnd.Next(3);
                switch (x)
                {
                    case 0:
                        if (game.Stage == HoldemStage.PreFlop)
                            return BrainAction.Fold;
                        return BrainAction.Call;
                    case 1: if (game.CanRaise(me))
                        {
                            return BrainAction.Raise;
                        }break;
                    case 2: return BrainAction.Call;
                }
            }
        }

        public override string ID { get { return "random"; } }

        #endregion

        public override string ToString()
        {
            return ID;
        }
    }

    public class CallBrains : HoldemBrains
    {

        #region IBrains Members

        public override BrainAction Think(IGameState game, GamePlayer me)
        {
            return BrainAction.Call;
        }

        public override string ID { get { return "call only"; } }

        #endregion
    }

    public class Raise50Brains : HoldemBrains
    {
        private double _level = 0.5;

        #region IBrains Members

        public Raise50Brains(double level)
        {
            _level = level;
        }

        public Raise50Brains()
        {
        }

        public override BrainAction Think(IGameState game, GamePlayer me)
        {
            game.CalcOdds(me);
            if (me.Odds.Value > _level && game.CanRaise(me))
                return BrainAction.Raise;
            else
                return BrainAction.Call;
        }

        public override string ID { get { return "raiseP>" + Math.Round(_level * 100); } }

        #endregion
    }

    public class RaiseVsBankBrains : HoldemBrains
    {
        #region IBrains Members
        private double _level = 0.3;
        public RaiseVsBankBrains()
        {

        }

        public RaiseVsBankBrains(double level)
        {
            _level = level;
        }

        public override BrainAction Think(IGameState game, GamePlayer me)
        {
            game.CalcOdds(me);
            double toRaise = game.GetRaiseAmount(me);
            if (me.Odds.Value > toRaise / game.Pot && game.CanRaise(me))
                return BrainAction.Raise;
            else if (game.Stage > HoldemStage.PreFlop)
                return BrainAction.Call;
            else if (me.Odds.Value > _level)
                return BrainAction.Call;
            else
                return BrainAction.Fold;
        }

        public override string ID { get { return "raiseVsBankFold<" + Math.Round(_level * 100); } }

        #endregion
    }


    public class PreBorman:HoldemBrains
    {
        CardStrength _cardStrength = new TabularCardStrength();
        OddsVsEstCardsCalc _est = new OddsVsEstCardsCalc();

        public PreBorman(CardStrength cs)
        {
            _cardStrength = cs;
            _est.Progress += new EventHandler<ProgressEventArgs>(_est_Progress);
        }

        void _est_Progress(object sender, ProgressEventArgs e)
        {
            if (e.Total % 100000 == 0)
                Out().WriteLine(_est.DebugState());
        }

        private double EstValue(double N, IGameState game)
        {
            // N=0..3
            if(N<0)
                N=0;
            if(N>3)
                N=3;
            if (N >= 3)
                return 12.5;
            if (N >= 2)
                return 25;
            if (N >= 1)
                return 50;
            return 100;
        }

        public int CalcAttackCounter(IGameState game)
        {
            int X = 0;
            foreach (GamePlayer p in game.Players.Active)
            {
                if (p.CallRaiseCount> 0)
                    X++;
            }
            return X;
        }

        public override BrainAction Think(IGameState game, GamePlayer me)
        {
            Out().WriteLine("Borman Step {0}", game.Step);

            foreach (GamePlayer p in game.Players.Active)
            {
                //*.BrainAction.*/double blindee = game.GetBlindAmount(p);
                double N = p.AttackValue;

                if (p != me)
                {
 
                    p.Info.EstCardValue.Value = EstValue(N, game);

                    p.Info.EstCards = _cardStrength.FromValue(p.Info.EstCardValue);

                }
                else
                {
                    me.Info.EstCardValue = _cardStrength.ValueOf(me.PocketCards);
                    me.Info.EstCards = new CardSetList(me.PocketCards);//_cardStrength.FromValue(p.Info.EstCardValue);
                    
                }
                Out().WriteLine("PL={4}; N={0:F2}; P={1:F2}; CRD={2}:{3}", N, p.Info.EstCardValue.Value, p.Info.EstCards.CardSetCount, (p.Info.EstCards as CardSetList).ToString(" ",80), p);
            }

            List<PlayerInfo> lpi = new List<PlayerInfo>();
            foreach (GamePlayer p in game.Players.Active)
                if(me!=p)
                    lpi.Add(p.Info);

            lpi.Sort(new PlayerInfo.CompareByValue());

            while (lpi.Count > 2)
                lpi.RemoveAt(lpi.Count - 1);

            lpi.Add(me.Info);

            Out().WriteLine("Simulating...");
            foreach (PlayerInfo pi in lpi)
            {
                Out().WriteLine("PL={0}; P={1:F2}; CRD={2}:{3}", pi.Player, pi.EstCardValue.Value, pi.EstCards.CardSetCount, (pi.EstCards as CardSetList).ToString(" ", 80));
            }
            
            _est.Calc(lpi, game.Table.VisibleBoardCards().ToString(), true);

            if (double.IsNaN(me.Odds.Value))
                throw new InvalidOperationException();

            if (game.RaiseCount == 0)
            {
                if (me.Info.EstCardValue.Value > 50)
                    return BrainAction.Fold;
                else if (me.Info.EstCardValue.Value > 25)
                    return BrainAction.Call;
                return BrainAction.Raise;
            }
           
            double toCall = game.GetCallAmount(me) / game.Pot;

            int X = CalcAttackCounter(game);
            double toRaise = 1.0 / X;

            Out().WriteLine("my new Odds={0:F2}% vs toCall={1:F2} vs toRaise(for {4})={2:F2} sim. total {3}", me.Odds.Value*100, toCall*100, toRaise*100, _est.Total,X);
            foreach (PlayerInfo pi in lpi)
            {
                Out().WriteLine("PL={0}; new Odds={1:F2}% ", pi.Player, 100*pi.Player.Odds.Value);
            }


            if (me.Odds.Value > toRaise)
                if(game.CanRaise(me))
                    return BrainAction.Raise;
                else
                    return BrainAction.Call;

            
            if(me.Position==1)  // BB
                if(game.RaiseCount==0)
                    return BrainAction.Call;

            if (me.Odds.Value > toCall)
                return BrainAction.Call;
            else
                return BrainAction.Fold;
        }

        public override string ID { get { return "preBorman"; } }
 
    }
}